{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Building a new plot\n",
    "-----------"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Following this guide**, you will create a new plot in no time. Remember to check the [introduction notebook to the framework](../basic-tutorials/Demo.ipynb) to understand that:\n",
    "- Your plot will support multiple plotting backends.\n",
    "- Your plot will only recompute what is needed when its inputs are updated.\n",
    "\n",
    "Let's get started!\n",
    "\n",
    "## The tools\n",
    "\n",
    "We provide you with a set of tools to create plots. The most basic ones are two of them: `get_figure` and `plot_actions`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sisl.viz import get_figure, plot_actions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "They are what support the multibackend framework. Let's try them out:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We create an action.\n",
    "action = plot_actions.draw_line(x=[1, 2], y=[3, 4], line={\"color\": \"red\"})\n",
    "\n",
    "# And then we plot it in a figure\n",
    "get_figure(backend=\"plotly\", plot_actions=[action])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Simple, isn't it?\n",
    "\n",
    "As you might have imagined, we can ask for a matplotlib figure:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "get_figure(backend=\"matplotlib\", plot_actions=[action])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A plot function\n",
    "\n",
    "It now feels reasonable to pack this very cool implementation this into a function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def a_cool_plot(color=\"red\", backend=\"plotly\"):\n",
    "    action = plot_actions.draw_line(x=[1, 2], y=[3, 4], line={\"color\": color})\n",
    "\n",
    "    return get_figure(backend=backend, plot_actions=[action])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And just like that, **you have your multi framework plot function**. It would be a shame to leave it unused."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a_cool_plot(color=\"green\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What is there left to do then? Remember that we wanted our plot to be a workflow, and currently it isn't.\n",
    "\n",
    "## From function to `Plot`\n",
    "\n",
    "To convert our function to a workflow, we need to introduce a new tool, `Plot`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sisl.viz import Plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is just an extension of sisl's `Workflow` class (see `sisl.nodes` documentation), so creating a `Plot` from a function is straightforward:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "CoolPlot = Plot.from_func(a_cool_plot)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now visualize our workflow!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "CoolPlot.network.visualize(notebook=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There we go, our first multi-backend, updatable `Plot` :)\n",
    "\n",
    "Let's use it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot = CoolPlot(color=\"blue\")\n",
    "plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now, the moment we've all been waiting for. Let's update our plot: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.update_inputs(backend=\"matplotlib\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Additional methods\n",
    "------"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It might be useful sometimes to provide helper methods so that the users can interact quickly with your plot. E.g. to change inputs or to extract some information from it.\n",
    "\n",
    "In that case, you'll just have to define the plot class with `class` syntax and write the methods as you always do:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CoolPlot(Plot):\n",
    "    # The function that this workflow will execute\n",
    "    function = staticmethod(a_cool_plot)\n",
    "\n",
    "    # Additional methods.\n",
    "    def color_like(self, object):\n",
    "        \"\"\"Uses the latest AI to change the color of the plot matching a given object\"\"\"\n",
    "\n",
    "        color = None\n",
    "        if object == \"sun\":\n",
    "            color = \"orange\"\n",
    "        elif object == \"grass\":\n",
    "            color = \"green\"\n",
    "        else:\n",
    "            raise ValueError(f\"The AI could not determine the color of {color}\")\n",
    "\n",
    "        return self.update_inputs(color=color)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And then you just use it as you would expect:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot = CoolPlot()\n",
    "plot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "nbsphinx-thumbnail"
    ]
   },
   "outputs": [],
   "source": [
    "plot.color_like(\"grass\").show(\"png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Complex plots\n",
    "-------------\n",
    "\n",
    "As you probably have noticed, you can go as complex as you wish inside your plot function. If you want to convert it to a `Plot` however, it is important that you encapsulate sub-functionalities into separate functions so that the workflow doesn't become to complex, storing useless data and adding too much overhead (this is generic advice for `Workflow`s).\n",
    "\n",
    "In `sisl.viz`, you will find plenty of helper functions, specially in `sisl.viz.processors`, that you might benefit from. You might want to check the already implemented plots in `sisl.viz.plots` for inspiration."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
